package p2p;

import java.net.DatagramPacket;
import java.util.List;
import java.util.StringTokenizer;

/** Thread that "listens" to the control port.
 * @author Bruno
 */
public class ControlPortThread extends Thread {

    @Override
    public void run() {
        listenControlPort();
    }

    private void listenControlPort() {
        byte[] buffer = new byte[bufferSize];
        DatagramPacket packet;
        String recv;

        // Continuously receive packets
        while (true) {
            // Receive the packet
            packet = new DatagramPacket(buffer, buffer.length);
            try {
                Global.controlSocket.receive(packet);
                recv = new String(packet.getData(), 0, packet.getLength());

                // Check packet type
                if (recv.startsWith("SEARCH")) {
                    processSearchMessage(recv);
                } else if (recv.startsWith("FOUND")) {
                    SearchList.processFoundMessage(recv);
                } else if (recv.startsWith("GET")) {
                    processGetMessage(recv);
                }
            } catch (Exception ex) {
                // Ignore
            }
        }
    }

    private void processSearchMessage(String msg) {
        StringTokenizer tok = new StringTokenizer(msg);
        tok.nextToken(); // "SEARCH" text

        if (tok.hasMoreTokens()) { // search-id
            String id = tok.nextToken();
            if (id.equals(SearchList.getLastSearchID())) // SEARCH message sent from this process, exit
            {
                return;
            }

            if (tok.hasMoreTokens()) { // keywords
                // Recover the filter from the tokens
                String filter = tok.nextToken();
                while (tok.hasMoreTokens()) {
                    filter += " " + tok.nextToken();
                }

                // Get P2PFiles that match the filter
                List<P2PFile> files = P2PFileList.searchFiles(filter);

                // Send FOUND messages (if any file found)
                for (P2PFile file : files) {
                    if (file.getState() != P2PFile.STATE_PAUSED && file.hasChunksAvailable()) { // Answer only if at least 1 chunk is available and if the user hasn't paused sharing this file
                        String message = "FOUND " + id + " " + file.getHash() + " " + file.getSize() + " " + file.getName();
                        DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), Global.multicastInetAddr, Global.controlPort);
                        try {
                            Global.controlSocket.send(packet);
                        } catch (Exception ex) {
                            // Ignore
                        }
                    }
                }
            }
        }
    }

    private void processGetMessage(String msg) {
		StringTokenizer tok = new StringTokenizer(msg);
		tok.nextToken(); // "GET" text

		if (tok.hasMoreTokens()) { // hash
			String hash = tok.nextToken();

			P2PFile fileToShare = P2PFileList.getFileByHash(hash);

			if (fileToShare != null && fileToShare.getState() != P2PFile.STATE_PAUSED) {
				// Check to see if there any requested chunks are avaiable

				/* First see if the message received defines the
				 * chunks requested.  This is always a string that can
				 * have 3 format:
				 *
				 * 1. A single number. Ex: 7
				 * 2. A list of comma separated values. Ex: 4,6,2,7,1
				 * 3. A range of numbers. Ex: 5-10
				 *
				 * The important thing to remember is that they're
				 * always just ONE string, for example, in format 2
				 * there isn't a space after the commas.
				 */
				if (tok.hasMoreTokens()) { // Chunks requested
					String chunks = tok.nextToken();

					// Format 1
					if (HelperGetFunctions.isNumber(chunks)) {
						int chunkRequested = Integer.parseInt(chunks);

						fileToShare.addRequestedChunk(chunkRequested);

						/*if (fileToShare.isChunkAvailable(chunkRequested)) {
							// Enviar chunk

							byte[] data = fileToShare.getChunk(chunkRequested);
							NetworkChunk netChunk = new NetworkChunk(hash, chunkRequested, data);

							DatagramPacket packet = new DatagramPacket(netChunk.getBuffer(), netChunk.getBuffer().length);
							try {
								Global.dataSocket.send(packet);
							} catch (IOException ex) {
								Logger.getLogger(ControlPortThread.class.getName()).log(Level.SEVERE, null, ex);
							}
						}*/
					} // Format 2
					else if (HelperGetFunctions.isCSV(chunks)) {
						List<Integer> CSVOfChunks = HelperGetFunctions.convertCSVToList(chunks);
						fileToShare.addRequestedChunks(CSVOfChunks);
					} // Format 3
					else if (HelperGetFunctions.isRange(chunks)) {
						/* NOTE: isRange only returns true when the
						 * left value of the range is smaller than the
						 * value on the right.
						 *
						 * Example: 3-10 returns true but 10-3 returns false
						 *
						 * This means that the program doesn't need to
						 * check which of the values is the smallest
						 */
						String[] splittedRange = chunks.split("-");

						int leftValue = Integer.parseInt(splittedRange[0]);
						int rightValue = Integer.parseInt(splittedRange[1]);
						for(Integer i = leftValue; i <= rightValue; i++)
							fileToShare.addRequestedChunk(i);
					} else {
						System.out.println("Problem found while reading the requested chunks of a GET request");
					}
				}

			}
		}
    }
	
    static final private int bufferSize = 2 * 32 + Global.chunkSize;
}
